// bs_kde.cpp : Defines the entry point for the console application.
//

#include <iostream> 
#include <string> 
#include <highgui.h> 
#include <cv.h> 
#include <math.h> 
#include <stdlib.h> 
#include <ctime> 
#include	<stdio.h>

const double e = 2.7183; 
const double segama = 30; 
const double pi = 3.14; 
double temp; 
const int numFrame = 64; 
const int numInitial = 1; 
const int numMRF = 5; 
const int num_write = 500; 
const double threshold = /*0.58*/0.4; 
const double alpha = 0.05/*0.07*/; 

using namespace std; 

//  return frame_result; 
//} 

double gaussian_value(int value) 
{ 
	double index = -pow((double)value, 2)/(2 * pow(segama, 2)); 
	temp = pow(e, index)/numFrame; 
	return temp; 
} 


int main(int argc, char **argv) 
{ 
	//set up windows 
	cvNamedWindow("origin", CV_WINDOW_AUTOSIZE); 
	cvNamedWindow("result", CV_WINDOW_AUTOSIZE); 
	double hash[256]; 

	for(int i = 0; i < 256; ++i) 
		hash[i] = gaussian_value(i); 

	CvCapture* capture = NULL;
	if (argc < 2)
	{
		cout << "No video file!" << endl;
		return -1;
	}
	if ((capture = cvCaptureFromAVI(argv[1]))==NULL)
	{
		cout << "Can't open this video file!" << endl;
		return -2;
	}
	//capture = cvCreateFileCapture( "http://vsn3-iss.bu.edu/mjpg/video.mjpg" ); 

	IplImage* frame = NULL; //original images of video 
	IplImage* frame_gray = NULL; //original images of video of gray level 
	IplImage* frame_temp = NULL; //next frame to be computed 


	IplImage* frame_result = NULL; // the result after background subtraction 
	IplImage* chain[numFrame]; //save the images to compute the kernel 
	IplImage* diff[numFrame]; //save the absolute difference 
	IplImage* phi[numFrame]; //save the MRF temp results 
	IplImage* dividend;  
	//IplImage* abc; 
	IplImage* frame_left_shift = NULL; //save the image with 1 pixel shifted to the left 
	IplImage* frame_right_shift = NULL; //save the image with 1 pixel shifted to the right 
	IplImage* frame_up_shift = NULL; //save the image with 1 pixel shifted to the up 
	IplImage* frame_down_shift = NULL; //save the image with 1 pixel shifted to the down 
	IplImage* frame_MRF = NULL; //save the MRF 
	IplImage* frame_diff = NULL; //save the difference of the image 

	frame = cvQueryFrame(capture); 
	CvSize size = {frame->width, frame->height}; 
	CvSize size_shift = {frame->width, frame->height}; 
	dividend = cvCreateImage(size, IPL_DEPTH_8U, 1); 
	frame_diff = cvCreateImage(size, IPL_DEPTH_8U, 1); 

	frame_MRF = cvCreateImage(size_shift, IPL_DEPTH_8U, 1); 


	CvScalar value = {numFrame, 0, 0, 0}; 

	cvSet(dividend, value); 

	for(int i = 0; i < numFrame; ++i) 
		phi[i] = cvCreateImage(size, IPL_DEPTH_8U, 1); 

	for(int num = 0; num < numFrame; ++num) 
	{ 
		int count = 0; 
		chain[num] = cvCreateImage(size, IPL_DEPTH_8U, 1); 
		//cvSetZero(frame_temp); 
		while(1) 
		{ 
			frame = cvQueryFrame(capture); 
			if(count == 0) 
			{ 
				frame_gray = cvCreateImage(size, IPL_DEPTH_8U, 1); 
				cvCvtColor(frame, frame_gray, CV_RGB2GRAY); 
				chain[num] = frame_gray; 
			} 
			count++; 
			if(count == 1) 
				break; 


			cvWaitKey(37); 
			//count++; 
			//if(count == numInitial) 
			//  break; 
		} 
	}// initialize 

	//frame_gray = cvCreateImage(size, IPL_DEPTH_8U, 1); 
	for(int i = 0; i < numFrame; ++i) 
		diff[i] = cvCreateImage(size, IPL_DEPTH_8U, 1); 


	int count_write = 0; 
	time_t TimeStart, TimeEnd, TimeUsed; 
	int time[1500]; 
	int num_time = 0; 

	frame_gray = cvCreateImage(size, IPL_DEPTH_8U, 1); 
	while(1) 
	{ 
		frame = cvQueryFrame(capture); 
		TimeStart = clock(); 
		cvCvtColor(frame, frame_gray, CV_RGB2GRAY); 

		for(int i = 0; i < numFrame; ++i) 
			cvAbsDiff(frame_gray, chain[i], diff[i]); 

		int num = 0; 
		frame_result = cvCreateImage(size, IPL_DEPTH_8U, 1); 
		for(int i = 0; i < frame_gray->width; ++i) 
		{ 
			for(int j = 0; j < frame_gray->height; ++j) 
			{     
				double temp = 0; 
				for(int k = 0; k < numFrame; ++k) 
				{ 
					(frame_result->imageData + frame_result->widthStep * j)[i] = 1; 
					int value = (diff[k]->imageData + diff[k]->widthStep * j)[i]; 
					temp += hash[value]; 
					/*temp += gaussian_value(value);*/ 
					if(temp > threshold) 
					{ 
						(frame_result->imageData + frame_result->widthStep * j)[i] = 0; 
						break; 
					} 
				} 
			} 
		} 


		cvSetZero(frame_diff); 
		for(int i = 0; i < numFrame; ++i) 
		{ 
			cvDiv(diff[i], dividend, phi[i], 1); 
			cvAdd(phi[i], frame_diff, frame_diff); 
		} 

		for(int k = 0; k < numMRF; ++k) 
		{ 
			for(int i = 1; i < frame->width - 1; ++i) 
				for(int j = 1; j < frame->height - 1; ++j) 
					(frame_MRF->imageData + frame_MRF->widthStep * j)[i] = 
					(frame_result->imageData + frame_result->widthStep * (j - 1))[i] + (frame_result->imageData + 
					frame_result->widthStep * (j + 1))[i] + (frame_result->imageData + frame_result->widthStep * j)[i 
					+ 1] + (frame_result->imageData + frame_result->widthStep * j)[i - 1]; 


			//cvAdd(frame_left_shift, frame_right_shift, frame_MRF); 
			//cvAdd(frame_MRF, frame_up_shift, frame_MRF); 
			//cvAdd(frame_MRF, frame_down_shift, frame_MRF); 

			//cvSetZero(frame_diff); 
			//for(int i = 0; i < numFrame; ++i) 
			//{ 
			//  cvDiv(diff[i], dividend, phi[i], 1); 
			//  cvAdd(phi[i], frame_diff, frame_diff); 
			//} 

			cvSetZero(frame_result); 
			for(int i = 0; i < frame->width; ++i) 
				for(int j = 0; j < frame->height; ++j) 
					if(pow((double)(frame_diff->imageData + frame_diff->widthStep * j)[i], 
						2) > 2 * pow(segama, 2) * (2.5 -  2 * (frame_MRF->imageData + frame_MRF->widthStep * (j + 1))[i + 1])) 
						if(k != numMRF - 1)
							(frame_result->imageData + frame_result->widthStep * j)[i] = 1;
						else
							(frame_result->imageData + frame_result->widthStep * j)[i] = 255;
		} 
		//cvDilate(frame_result, frame_result); 
		//cvErode(frame_result, frame_result); 
		cvShowImage("origin", frame); 
		/*cvShowImage("test", frame_left_shift);*/ 
		cvShowImage("result", frame_result); 



		for(int i = 0; i < frame_gray->width; ++i) 
		{ 
			for(int j = 0; j < frame_gray->height; ++j) 
			{ 
				if((frame_result->imageData + frame_result->widthStep * j)[i] == 0) 
				{ 
					(chain[num%numFrame]->imageData + chain[num%numFrame]->widthStep * j)[i] 
					= (frame_gray->imageData + frame_gray->widthStep * j)[i]; 
				} 
				else 
				{ 
					(chain[num%numFrame]->imageData + chain[num%numFrame]->widthStep * j)[i] 
					= (int)((1 - alpha) * (double)(chain[num%numFrame]->imageData + chain[num%numFrame]->widthStep * 
						j)[i] + alpha * (double)(frame_gray->imageData + frame_gray->widthStep * j)[i]); 
				} 
			}
		}
		//chain[num%numFrame] = frame_gray; 
		TimeEnd = clock(); 
		//time[num_time] = TimeEnd - TimeStart; 

		cout << "The processing time is: " << TimeEnd - TimeStart << endl; 
		//cvWriteFrame(writer1, frame); 
		//cvWriteFrame(writer2, frame_result); 

		cvReleaseImage(&frame_result); 

		num++; 
		//if(num == numFrame) 
		//  num = 0; 

		//num_time++; 
		//if(num_time == 1500) 
		//  break; 

		if(cvWaitKey(100) == 27) //press ESC to quit, fps = 33; 
			break;   
		}

		//Release the memory 
		//cvReleaseVideoWriter(&writer1); 
		//cvReleaseVideoWriter(&writer2); 
		//char* filename = "D:\\ec720\\data\\KDE_MRF_1st_16F_4I_without_boundary_480.txt"; 
		//FILE* fp = fopen(filename, "w"); 
		//for(int i = 0; i < 1500; ++i) 
		//  fprintf(fp, "%d\n", time[i]); 
		//fclose(fp); 
		cvReleaseCapture(&capture); 
		//cvReleaseImage(&frame); 
		cvReleaseImage(&frame_gray); 
		//cvReleaseImage(&frame_next); 
		//cvReleaseImage(&frame_result); 
		//cvReleaseImage(&chain[numFrame]); 
		cvDestroyWindow("origin"); 
		//cvDestroyWindow("processing"); 
		cvDestroyWindow("result"); 

		return 0; 
}
